#include "StreamerCurtainPlay.h"
#include "mediaPriv.h"
#include "CurtainActManager.h"
#include <sys/mman.h>
#include "Mp4ClientStream.h"
#include "ZipUtil.h"
#include "Protocol/ProtocolApi.h"
#include "Network/Buffer.h"
#include "UsbAVStream.h"



extern "C"
{
	#include "md5.h"
}

using namespace Protocol;
using namespace std;
using namespace Fdt;


namespace Media{

StreamerCurtainPlay*       StreamerCurtainPlay::sm_streamercurtainPlayIns;
std::mutex 		           StreamerCurtainPlay::sm_insMutex;

StreamerCurtainPlay* StreamerCurtainPlay::instance()
{
	if(sm_streamercurtainPlayIns == nullptr)
    {
        std::lock_guard<std::mutex> guard(sm_insMutex);
        if(sm_streamercurtainPlayIns == nullptr)
        {
            sm_streamercurtainPlayIns = new StreamerCurtainPlay();
        }
    }
    return sm_streamercurtainPlayIns;
}

StreamerCurtainPlay::StreamerCurtainPlay() 
{ 

}

StreamerCurtainPlay::~StreamerCurtainPlay() 
{ 

}

bool StreamerCurtainPlay::streamerDescribeCheckout(Json::Value& streamerTable, std::string url, std::string fileName)
{
    if(!streamerTable.isMember("Streamer"))
	{
        LOG_PrintError("json params error\n");
        return false;
	}

	Json::Value type       = streamerTable["type"];
	Json::Value streamInfo = streamerTable["Streamer"];
    Json::Value detailInfo = Json::nullValue;
	for(uint i = 0; i < streamInfo.size(); ++i)
	{
        detailInfo = streamInfo[i];
        if (!detailInfo.isMember("filename") 
          || !detailInfo.isMember("showtime"))
        {
            LOG_PrintError("json params error\n");
            return false;
        }

        if (!detailInfo.isMember("pattern"))
        {
			if (type == "localvideo") 
            {
                detailInfo["pattern"] = "VIDEOFILE";
                detailInfo["filename"] = m_streamerPath + detailInfo["filename"].asCString();
            }
            else 
            {
                detailInfo["pattern"] = "ONLINEMP4";
            }
        }

        if(!url.empty() && !fileName.empty())
        {
            if(detailInfo["filename"] == url)
            {
                detailInfo["filename"] = fileName;

                detailInfo["pattern"] = "VIDEOFILE";
            }
        }       

        streamInfo[i] = detailInfo;
	}

	if((streamInfo.size() == 1)
	  && (detailInfo["pattern"] == "VIDEOFILE"))
	{
        Mp4ClientStream::instance(MP4_LOCAL_STREAMER)->setRepetInfo(true);
	}
	else
	{
        Mp4ClientStream::instance(MP4_LOCAL_STREAMER)->setRepetInfo(false);
	}
	

	streamerTable["Streamer"] = streamInfo;

	return true;
}

bool StreamerCurtainPlay::StreamerTableCheckout(Json::Value curtainTable)
{
    if (!curtainTable.isMember("StreamSource"))
    {
        LOG_PrintError("curtainTable StreamSource get faild!!\n");
        return false;
    }

    Json::Value streamSource = curtainTable["StreamSource"];
    if (!streamSource.isArray() || streamSource.size() < 2)
    {
        LOG_PrintError("streamSource isArray faild!!\n");
        return false;
    }
    
    Json::Value stream = Json::nullValue;
    for(uint i = 0; i < streamSource.size(); ++i)
    {
        stream = streamSource[i];
        if(stream.isMember("VideoMixer"))
        {
            stream = stream["VideoMixer"];
        }
        else if(stream.isMember("AudioMixer"))
        {
            stream = stream["AudioMixer"];
        }
        else
        {
            LOG_PrintError("VideoMixer || AudioMixer get faild!!\n");
            return false;
        }
        
        if (!stream.isMember("Detail"))
        {
            LOG_PrintError("Json Detail get faild!!\n");
            return false;
        }

        stream = stream["Detail"];
        if (!stream.isArray())
        {
            LOG_PrintError("Json isArray get faild!!\n");
            return false;
        }

        Json::Value detail = Json::nullValue;
        for(uint j = 0; j < stream.size(); ++j)
        {
            detail = stream[j];

            if (!detail.isMember("Stream"))
            {
                LOG_PrintError("Json Stream get faild!!\n");
                return false;
            }

            detail = detail["Stream"];
            if (!detail.isMember("Type") || !detail.isMember("Content"))
            {
                LOG_PrintError("Json Type  Content get faild!!\n");
                return false;
            }
        }
    }

    return true;
}

bool StreamerCurtainPlay::updateStreamerPlay()
{
    Json::Value streamer = m_streamTable["Streamer"];
    Json::Value streamInfo = streamer[m_curPlayIndex];
    m_curPlayIndex = (m_curPlayIndex + 1) % streamer.size();
    
    if(streamInfo.isMember("vpnaddr")){
        m_mp4VpnAddr = streamInfo["vpnaddr"].asString();
    }
       
    std::string filePath = streamInfo["filename"].asString();
    std::string pattern = streamInfo["pattern"].asString();

    // if(m_curfilePath == filePath
      //   && compareMd5(filePath, m_curfileMd5.c_str()))
    // {
    //     tracef("updateStreamerPlay not need update\n");
        //     return true;
    // }
    
    m_curfilePath = filePath;

    if (!m_curtainTable["StreamSource"].isArray() || m_curtainTable["StreamSource"].size() < 2)
    {
        LOG_PrintError("m_curtainTable isArray faild!!\n");
        return false;
    }

    /* content and type update */    
    Json::Value stramInfo;
    stramInfo["Stream"]["Channel"] = 0;
    stramInfo["Stream"]["Type"] = pattern;
    stramInfo["Stream"]["Content"] = filePath;

    m_curtainTable["StreamSource"][0]["VideoMixer"]["Detail"][0]["Stream"] = stramInfo["Stream"];
    
    std::string uacPos; 
    if(UsbAVStream::instance()->getUacOutStatus(uacPos))
    {  
        m_curtainTable["StreamSource"][1]["AudioMixer"]["Detail"][0] = stramInfo; //usb输出
    }
    else
    {
        m_curtainTable["StreamSource"][0]["AudioMixer"]["Detail"][0] = stramInfo;//hdmiout输出
    }
    


    //CurtainActManager::instance()->setCurtainLayout(m_curtainTable, true);

    CurtainActManager::instance()->usbStreamCheck(m_curtainTable);
    CurtainActManager::instance()->parseCurtainLayout(m_curtainTable);
    
    ChangeStreamerNumAction(1);


    tracef("updateStreamerPlay need update\n");

    return true;
}

bool StreamerCurtainPlay::StartDynamicStreamer()
{ 
    if (!CurtainActManager::instance()->getSpecificNumCurtain(m_curtainTable, StreamerNum)) {
        LOG_PrintError("Default Streamer Curtain get faild!!\n");
        return false;
    }
    if(!StreamerTableCheckout(m_curtainTable))
    {
        LOG_PrintError("curtainTable checkout failed\n");
        return false; 
    } 

    m_palyDscribeMutex.lock();
    if(!CurtainActManager::instance()->getJsonTable(m_streamerDescPath, m_streamTable))
    {
        LOG_PrintError("Default Streamer Curtain get faild!!\n");
        m_palyDscribeMutex.unlock();
        return false;
    }
    m_palyDscribeMutex.unlock();

    if(!streamerDescribeCheckout(m_streamTable, "", ""))
    {
        LOG_PrintError("m_streamTable checkout failed\n");
        return false; 
    }

    m_palyDscribeMutex.lock();
	CurtainActManager::instance()->setJsonTable(m_streamerDescPath, m_streamTable);
	m_palyDscribeMutex.unlock();
	LOG_PrintWarn("m_streamTable write---> %s\n", m_streamTable.toStyledString().c_str());


    return true;
}

bool StreamerCurtainPlay::StartStaticStreamer(){
	//读取当前横幅配置信息，处理一下数据保证格式正确
	if(!m_streamTable.isMember("Streamer"))
	{
        stopStreamerCurtain();
        LOG_PrintError("json params error\n");
        return false;
	}
	Json::Value streamer = m_streamTable["Streamer"];
	LOG_PrintInfo("stream ---> %s\n", streamer.toStyledString().c_str());
	//设置index
	int index = 0;
	int64_t newTime = 0;
	//读取默认横幅场景
	Json::Value curtainTable;
	if(!CurtainActManager::instance()->getSpecificNumCurtain(curtainTable, StreamerNum)){
		LOG_PrintError("Default Streamer Curtain get faild!!\n");
		return false;
	}
	//循环播放场景
	while(m_streamerPlay){
		if(newTime <= 0){
			//修改图片的名字，拼接最终场景的json，下发场景，开始播放
			Json::Value  imageInfo = streamer[index];
			if(!imageInfo.isMember("filename")
			  || !imageInfo.isMember("showtime")
			  || !imageInfo.isMember("index"))
			{
                LOG_PrintError("json params error\n");
                continue;
			}
			
			//LOG_PrintInfo("reset image name start json--->%s\n", curtainTable.toStyledString().c_str());
			//LOG_PrintInfo("imageinfo ---> %s\n", imageInfo.toStyledString().c_str());
			curtainTable["StreamSource"][0]["VideoMixer"]["Detail"][0]["Stream"]["Content"] = imageInfo["filename"].asString();
            curtainTable["StreamSource"][0]["VideoMixer"]["Detail"][0]["Stream"]["Type"] = "PICTUREFILE";

            CurtainActManager::instance()->parseCurtainLayout(curtainTable);

			
            ChangeStreamerNumAction(1);
			//重新赋值下一次的index
			index = (index + 1) % streamer.size();
			newTime = imageInfo["showtime"].asInt64() * 1000;
		}
		newTime -= 200;
		std::this_thread::sleep_for(std::chrono::milliseconds(100 * 2));
	}
	m_streamerPlayThread = true;
	LOG_PrintInfo("StreamerCurtainPlay::StartStaticStreamer() end \n");
	return true;
}

bool StreamerCurtainPlay::ChangeStreamerNumAction(int action) {
    Json::Value root2 = Json::Value::null;
    root2["Number"] = StreamerNum;
    root2["PlayMode"] = 0;
    root2["Action"] = action;
    CurtainActManager::instance()->setPlayStrategy(root2);

    return true;
}

bool StreamerCurtainPlay::getDownloadUrl(std::vector<std::string>& urls)
{
    if(!m_streamTable.isMember("Streamer"))
	{
        LOG_PrintError("json params error\n");
        return false;
	}

	Json::Value type       = m_streamTable["type"];
    if (type == "localvideo") 
    {
        return false;
    }

	Json::Value streamInfo = m_streamTable["Streamer"];
    Json::Value detailInfo = Json::nullValue;
	for(uint i = 0; i < streamInfo.size(); ++i)
	{
        detailInfo = streamInfo[i];
        if (!detailInfo.isMember("filename")
          || !detailInfo.isMember("pattern"))
        {
            continue;
        }

        std::string filename = detailInfo["filename"].asString();
        if(detailInfo["pattern"].asString() == "ONLINEMP4")
        {
            auto it = std::find(urls.begin(), urls.end(), filename);
            if (it == urls.end())
            {
                urls.push_back(detailInfo["filename"].asString());
            }
        }     
	}
	
    return true;
}

    
bool StreamerCurtainPlay::clientSyncCloud(Json::Value describeTable){
    std::string sourName = describeTable["Streamer"][0]["filename"].asString();
    Json::Value notifyTable;
    ZipUtil ziputil;
    std::string reviewFile;
    unsigned int fileSize;
    std::string md5;
    std::string response;
    std::set<std::string> array;
    bool result = false;

    reviewFile = m_streamerPath + "image.zip";
    do{
        //确定上传的文件
        if(sourName.find(".jpg") != std::string::npos){
            notifyTable["Type"] = "static";
            for(int i = 0; i < describeTable["Streamer"].size(); i++){
                std::string newSource =  describeTable["Streamer"][i]["filename"].asString();
                array.insert(m_streamerPath + newSource);
            }
            array.insert("/mnt/media/streamer/describe");
        }else{
            notifyTable["Type"] = "dynamic";
            
            //需要修改文件重新压缩
            for(int i = 0; i < describeTable["Streamer"].size(); i++){
                sourName =  describeTable["Streamer"][i]["filename"].asString();
                std::string newSource = sourName.substr(0, sourName.find(".mp4")) + ".jpg";
                describeTable["Streamer"][i]["filename"] = newSource;
                array.insert(m_streamerPath + newSource);
            }
            CurtainActManager::instance()->setJsonTable("/mnt/media/streamer/describe1", describeTable);
            array.insert("/mnt/media/streamer/describe1");
        }
        if(!ziputil.compressFile(array, reviewFile)){
            LOG_PrintError("compressFile faild\n");
            break;
        }
        //获取MD5
        if(!ziputil.getMd5(reviewFile, fileSize, md5)){
            LOG_PrintError("getMd5 faild\n");
            break;
        }
        notifyTable["Md5"] = md5;
        LOG_PrintWarn("reviewFile: %s       notifyTable:%s\n", reviewFile.c_str(),  notifyTable.toStyledString().c_str());
        //请求上传文件的url
        Protocol::IProtocolInterface::GetInstance()->AuditoryworksSendRequest("NotiftyCloudUpdateStream", notifyTable.toStyledString(), response, 3);

        if(response.empty()){
            LOG_PrintError("AuditoryworksSendRequest NotiftyCloudUpdateStream faild\n");
            break;
        }

        bool isUploading = true;
        std::function<void (void)> stop_func;
        //上传文件
        static auto resultCb = [&isUploading](const SockException &ex){
            if (ex) {
                //网络相关的错误
                WarnL << "network err:" << ex.getErrCode() << " " << ex.what();
            } else {
                WarnL << "succeed"  << ex.what();
            }
            isUploading = false;
        };
        Json::Value repTable;
        Json::Reader reader;        
        if(!reader.parse(response, repTable)){
            break;
        }

        if(!IProtocolInterface::GetInstance()->HttpUploadFile(repTable["Url"].asString(), 
            reviewFile, resultCb, stop_func, true)){
            LOG_PrintError("HttpUploadFile faild     url: %s    reviewFile: %s\n", repTable["Url"].asString().c_str(), reviewFile.c_str());
            break;
        }
        while(isUploading && m_upload.load()){
            usleep(1000 * 100);

        }
        if(isUploading){
            while (!stop_func) {
                usleep(1000 * 300);
            }
            stop_func();
        }
        result = true;
    }while(false);
    m_upload.store(false);
    m_uploadThread.store(false);
    return result;
}


bool StreamerCurtainPlay::playStreamerCurtain(StreamerType type, std::string rootPath){
	//清除上一组播放的横幅
	stopStreamerCurtain();

    if(rootPath.empty()){
        if(m_rootPath.empty() || !DiskController::instance()->isMountPath(m_rootPath)){
            DiskController::instance()->setRealTimeRootPath();
            DiskController::instance()->getRealTimeRootPath(rootPath);
        }else{
            rootPath = m_rootPath;
        }
    }
    m_rootPath = rootPath;
    m_streamerPath = rootPath + STREAM_ROOT_DIR;
    m_streamerDescPath = rootPath + std::string(STREAM_ROOT_DIR) + std::string(DESCRIBE_FILENAME);


    
	//读取开始结束时间
	if(!CurtainActManager::instance()->getJsonTable(m_streamerDescPath, m_streamTable))
    {
        LOG_PrintError("Default Streamer Curtain get faild!!, Path=%s\n", m_streamerDescPath.c_str());
        return false;
    }
    LOG_PrintWarn("Default Streamer Describe=%s, Path=%s\n", m_streamTable.toStyledString().c_str(),m_streamerDescPath.c_str());
    

	if(!m_streamTable.isMember("relativestart")
	  || !m_streamTable.isMember("relativeend"))
	{
        LOG_PrintError("json params error\n");
        return false;
	}

	uint64_t curTime = vdo_get_time();
	uint64_t start =  m_streamTable["relativestart"].asUInt64() * 1000 + curTime;
	uint64_t end   =  m_streamTable["relativeend"].asUInt64() * 1000 + curTime;
	LOG_PrintInfo("start: %llu   end: %llu\n", start, end);
	if(end < start){
		LOG_PrintError("params error endtime < starttime \n");
		return false;
	}
	
    if (m_streamTable.isMember("type")
      && (m_streamTable["type"].asString() == "onlinevideo" ||
                 m_streamTable["type"].asString() == "localvideo"))
    {
        m_videoStreamer = true;
    }
    else
    {
        m_videoStreamer = false;
    }
    
    // if(type == CLIENT_STREAMER){
    //     m_uploadThread.store(true);
    //     m_upload.store(true);
    //     m_syncCloud = std::thread(&StreamerCurtainPlay::clientSyncCloud, this, m_streamTable);
    //     m_syncCloud.detach();
    // }
    

	//设置横幅播放的标志位
	m_streamer = true;
	auto lam = [this](uint64_t start, uint64_t end){			
		//监听，根据时间看是否执行开启横幅或者关闭横幅的操作
		while(m_streamer){
			//获取当前系统时间，如果时间大于要执行的任务，且任务没有开始执行就执行
			uint64_t time = vdo_get_time();
			if(m_streamerPlay == false && time > start){
				m_streamerPlay = true;
                if (m_videoStreamer)
                {
                    m_streamerPlayThread = true;
                    m_curPlayIndex = 0;
                    if(!StartDynamicStreamer())
                    {
                        LOG_PrintError("StartDynamicStreamer failed\n");
                        break;
                    }
                    thread streamerplay(&StreamerCurtainPlay::updateStreamerPlay, this);
                    streamerplay.detach();

                    std::vector<std::string> urls;
                    if(getDownloadUrl(urls)){
                        if(!urls.empty())
                        {
                            auto downloadResultCb = [this](bool result, std::string url, std::string fileName){

                                LOG_PrintWarn("result =%d, url = %s, fileName=%s\n", result, url.c_str(), fileName.c_str());
                                if(result > 0)
                                {
                                    changeDescribeInfo(url, fileName);
                                }
                            };

                            std::string path = m_streamerPath;

                            auto downloadStart = [downloadResultCb, urls, path]{
            
                                StreamerDownloadOnlineFile::instance(STREAMER)->start(urls, downloadResultCb, path);
                            };
                            thread downloadThread(downloadStart);
                            downloadThread.detach();
                        }
                    }
                }
                else
                {
                    thread streamerplay(&StreamerCurtainPlay::StartStaticStreamer, this);
                    streamerplay.detach();
                }
			}

			if(time > end && m_streamerPlay == true){
				thread stopStreamer(&StreamerCurtainPlay::stopStreamerCurtain, this);
				stopStreamer.detach();
			}
			std::this_thread::sleep_for(std::chrono::milliseconds(100 * 2));
		}
		m_streamerThread = true;
	};	
	thread streamer(lam, start, end);
	streamer.detach();
	return true;
}

bool StreamerCurtainPlay::stopStreamerCurtain(){
	lock_guard<mutex> lock(m_stopstreamLock);
	//如果当前正在监听前一组横幅的定时器需要停止
	if(m_streamer){
		m_streamer = false;
		while(!m_streamerThread){
			std::this_thread::sleep_for(std::chrono::milliseconds(100 * 2));
		}
		m_streamerThread = false;
	}
	//如果现在有横幅正在播放立即停止
	if(m_streamerPlay){
		m_streamerPlay = false;
		while(!m_streamerPlayThread){
			std::this_thread::sleep_for(std::chrono::milliseconds(100 * 2));
		}
		LOG_PrintInfo("stopStreamerCurtain start2\n");
		m_streamerPlayThread = false;

		//停止文件下载
        StreamerDownloadOnlineFile::instance(STREAMER)->stop();
        
		//停止场景播放
        ChangeStreamerNumAction(0);
	}
    if(m_uploadThread.load()){
        //设置让线程退出
        m_upload.store(false);
        while(m_uploadThread.load()){
            usleep(1000 * 100);
        }
    }
	return true;
}

bool StreamerCurtainPlay::stopUsbcardStreamer(){
    if(AIR_UDISK_PATH == m_rootPath){
        CurtainActManager::instance()->playDefaultCurtain();
    }
    return true;
}

bool StreamerCurtainPlay::getMp4VpnValue(std::string& mp4VpnAddr)
{
    mp4VpnAddr = m_mp4VpnAddr;

    return true;
}

bool StreamerCurtainPlay::compareMd5(std::string filename, const char *md5)
{
    bool ret = false;
	unsigned int size;
	unsigned char *pData;
	unsigned char outputTemp[16] = {0};
	const char *md = md5;

	if(filename.empty() || !md5)
	{
		errorf("params failed!\n");
		return false;
	}
	
	int fd = open(filename.c_str(),O_RDWR);
	if (fd < 0)
	{
		printf("open %d failed!\n", fd);
		return false;
	}
	size = lseek(fd, 0, SEEK_END);

	pData = (unsigned char *)mmap(NULL,
								  size,
								  PROT_READ,
								  MAP_PRIVATE,
								  fd,
								  0);
	md5_calc(outputTemp, pData, size);

	printf("md5 ------------------------ %s\n", md);
	uint8_t *mddd = (uint8_t *)md;
	printf("local md--->%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X \n", mddd[0],mddd[1], mddd[2], mddd[3], mddd[4],
				mddd[5], mddd[6], mddd[7], mddd[8], mddd[9], mddd[10], mddd[11], mddd[12], mddd[13], mddd[14], mddd[15]);
	printf("local md--->%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X \n", outputTemp[0],outputTemp[1], outputTemp[2], outputTemp[3], outputTemp[4],
				outputTemp[5], outputTemp[6], outputTemp[7], outputTemp[8], outputTemp[9], outputTemp[10], outputTemp[11], outputTemp[12], outputTemp[13], outputTemp[14], outputTemp[15]);

	if (memcmp(outputTemp, md5, 16) == 0)
	{
		LOG_PrintInfo("compareMd5 equal\n");
		ret = true;
	}
	else
	{	
        m_curfileMd5 = (char*)outputTemp;
    }
    
	munmap((void *)pData, size);
    close(fd);
	return ret;
}


bool StreamerCurtainPlay::playNextStreamer()
{
    tracef("playNextStreamer enter\n");    
    if(!m_streamer)
    {
        return false; 
    }

    if(!StartDynamicStreamer())
    {
        LOG_PrintError("StartDynamicStreamer failed\n");
        return false;
    }

    thread streamerplay(&StreamerCurtainPlay::updateStreamerPlay, this);
    streamerplay.detach();

    return true;   
}

bool StreamerCurtainPlay::changeDescribeInfo(std::string url, std::string fileName)
{
    m_palyDscribeMutex.lock();
    if(!CurtainActManager::instance()->getJsonTable(m_streamerDescPath, m_streamTable))
    {
        LOG_PrintError("Default Streamer Curtain get faild!!\n");
        m_palyDscribeMutex.unlock();
        return false;
    }
    m_palyDscribeMutex.unlock();

    if(!streamerDescribeCheckout(m_streamTable, url, fileName))
    {
        LOG_PrintError("m_streamTable checkout failed\n");
        return false; 
    }

    m_palyDscribeMutex.lock();
	CurtainActManager::instance()->setJsonTable(m_streamerDescPath, m_streamTable);
	m_palyDscribeMutex.unlock();
	LOG_PrintWarn("m_streamTable write---> %s\n", m_streamTable.toStyledString().c_str());
	
    return true;
}

}

